dojo.provide("lorelei.mml.player");
// Lorelei MML Player script
// Copyright (c)2008-2009 Logue <http://logue.be/> All rights reserved.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Lorelei JSMML Player based on JSMML(http://coderepos.org/share/wiki/JSMML)

lorelei.mml.player = {
	meta: {
		"@prefix": "<http://purl.org/net/ns/doas#>",
		"@about": "<http://mabinogi.logue.be/js/lolerei/mml/player.js>", a: ":JavaScript",
		title: "Lorelei MML Player script",
		created: "2008-07-27", release: {revision: "1.6.8", created: "2009-10-11"},
		author: {name: "Logue", homepage: "<http://logue.be/>"},
		license: "<http://www.gnu.org/licenses/gpl-3.0.html>",
		acknowledgement: {name :"Yuichi Tateno", homepage :"<http://rails2u.com/>"}
	},
	// 初期設定
	init :function(){
		if (location.protocol == 'file:') console.warn('This script will not run file:// protocol.');
		this.PlayLabel = this.PlayButton.label;
		
		if(!this.PlayButton) console.error('"lorelei.mml.player.PlayButton" has not defined. Please insert Play Button DOM. example: lorelei.mml.player.PlayButton = dijit.byId(\'Play\');');
		if(!this.StopButton) console.error('"lorelei.mml.player.StopButton" has not defined. Please insert Stop Button DOM. example: lorelei.mml.player.StopButton = dijit.byId(\'Stop\');');
		if(!this.Container) console.error('"lorelei.mml.player.Container" has not defined. Please insert Player Container DOM. example: lorelei.mml.player.Container = dojo.byId(\'media\');');

		if(!this.type) this.type='midi';
		if(!this.inst) this.inst = 1;
		if(!this.PauseLabel) this.PauseLabel = 'Pause';
		if(!this.ChangeLabel) this.ChangeLabel = 'Change';
		this.changed = true;

		this.PlayButton.attr('onClick',function(){
			lorelei.mml.player.play();
		});
		this.StopButton.attr('onClick',function(){
			lorelei.mml.player.stop();
		});
		this.StopButton.attr('disabled',true);
	},
	// 初期化
	setup : function(){
//		console.log('setup');
		// DOMの設定
		if(!this.id) console.info('"lorelei.mml.player.id" has not defined. Change to MML Composer Mode.');

		if (this.id){
			// MMLデーターと楽器指定を削除
			this.mmls = null;
		}else{
			this.inst = 1;	// default to Lute
			try{
				this.mmls = lorelei.mml.checker.getMML();
				this.inst = dijit.byId('instrument').value;
			}catch(e){
				//null
				alert("wow!\n"+e);
			}
		}

		if (this.changed == true){
			this.score = null;
		}

		this.embed = null;
		// 再生方式
		if (this.type == 'midi'){
			this.midi();
		}else{
			this.flmml();
		}

		this.PlayButton.attr('label',this.PauseLabel);
		this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerPause');
	},
	// 演奏方式セレクタ（ラジオボタンやポップアップメニューで定義。）
	selector: function(type){
		this.PlayButton.attr('disabled',false);
		if (this.type == type){
			this.changed = false;
			if (this.isPause == true){
				this.PlayButton.attr('label',this.PauseLabel);
				this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerPause');
			}else{
				this.PlayButton.attr('label',this.PlayLabel);
				this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerPlay');
			}
		}else{
			this.type = type;
			this.changed = true;
			this.init();
			// this.PlayButton.attr('label',this.ChangeLabel);
			this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerChange');
		}
	},
	// FLMML/JSMML用の処理
	// lorelei.mml.player.flmml(再生したいMMLの記事のID,JSMMLか？,MMLデーター,楽器ID)
	flmml: function(){
		dojo.require("dojox.embed.Flash");

		if (dojox.embed.Flash.installed == false){
			this.progress(true);
			if(window.confirm(message['flash_required'])) location.href = "http://www.adobe.com/go/getflashplayer";
		}else{
			this.progress();
			player = lorelei.mml.player;
			
			if (this.score == undefined){	// 楽譜がキャッシュされていない場合JSONでデーターを取得。
				if (this.id == null){
					// 楽器のFLMML用の定義データーを楽器のIDから取得。（例：id=1の場合リュート）
					// lorelei.mml.player.inst = 楽器
					// lorelei.mml.player.mmls = [ch1,ch2,ch3]という形式のMabiMML
					dojo.xhrGet({
						url: '../../instruments/json/'+this.inst,
						handleAs: "json",
						timeout: 5000, // Time in milliseconds
						// The LOAD function will be called on a successful response.
						handle: function(response, ioArgs) {
			//				console.log("dojo.xhrGet: Success!");
			//				console.dir(response);
							var inst = response[0].Instrument;
							player.score = player.MabiMML2MML(0,inst,player.mmls,64,inst.isDram);
							player.flmml_callback();
							return response;
						},
						error: function(response, ioArgs) { 
							console.error("dojo.xhrGet: Failure");
							console.log("Responce: "+response);
							console.log("HTTP status code: ", ioArgs.xhr.status);
							return response;
						}
					});
				}else{
					// MMLをJSON形式で取得。全トラックデーター、楽器情報も含む。responseの中身をconsloe.dirで見てみ
					dojo.xhrGet({
						url: '../../mmls/export/'+this.id+'/json',
						handleAs: "json",
						timeout: 5000, // Time in milliseconds
						// The LOAD function will be called on a successful response.
						handle: function(response, ioArgs) {
//							console.log("dojo.xhrGet: Success!");
							// toFLMMLstr関数へ
							player.score = player.toFLMMLstr(response);
							player.flmml_callback();
							return response;
						},
						error: function(response, ioArgs) { 
							console.error("dojo.xhrGet: Failure");
							console.log("Responce: "+response);
							console.log("HTTP status code: ", ioArgs.xhr.status);
							return response;
						}
					});
				}
			}else{
				this.flmml_callback();
			}
		}
	},
	// FLMML/JSMMLを描画
	flmml_callback: function(){
//		console.log('callback Function');

		var embed, flash;

		if (this.type != 'flmml'){
			this.type = 'jsmml';
			flash = dojo.moduleUrl("lorelei", "resource/JSMML.swf");
			embed = new dojox.embed.Flash({
				id: "embed",
				expressInstall:true,
				path:flash,
				width:0,
				height:0,
				params:{
					swfLiveConnect:true,
					allowScriptAccess:'always'
				}
			},this.Container);
			this.embed = embed;

			dojo.connect(embed,'onReady',function(){
	//			console.log('JSMML is Ready',this);
				lorelei.mml.player.loaded();
			});
			
			// バグ
			dojo.connect(embed,'onFinish',function(){
				console.log('JSMML is Finish');
				lorelei.mml.player.stop();
			});
			
		}else{
			flash = dojo.moduleUrl("lorelei", "resource/flmml.swf");
			embed = new dojox.embed.Flash({
				id: "embed",
				expressInstall:true,
				path:flash,
				width:480,
				height:320,
				params:{
					swfLiveConnect:true,
					flashvars : "mml="+encodeURIComponent(this.score)
				}
			},this.Container);

			this._uNum = null;
			this.embed = dojo.byId('embed');
		}
		if (dojo.byId('score')) dojo.byId('score').value = this.score;
	},
	// QuickTimeによるMIDI再生
	midi: function(){
		this.progress();
		this.type = 'midi';
		this.changed = false;

		dojo.require("dojox.embed.Quicktime");
		var embed;

		if (this.id == null){
//			dojo.require("dojox.encoding.base64");
			dojo.require("lorelei.deflate");
			dojo.require("lorelei.base64");
			var data =  this.inst+';'+ this.mmls.join(";") + ";";
			data = lorelei.deflate(data);	// gZip圧縮
//			str = dojox.encoding.base64.encode(data);
			str = lorelei.base64.encode(data);
			this.request = '/mmls/composer?'+'q='+encodeURIComponent(str);
		}else{
			this.request = '../../mmls/export/'+this.id+'/midi';
		}

		embed = new dojox.embed.Quicktime({ 
			path: this.request,
			id: "embed",
			width: 0,
			height: 0,
//			style: "behavior:url(#qt_event_source);",
			params :{
				postdomevents : true,
				EnableJavaScript : true,
				autostart: true
			}
		},this.Container);
		this.embed = embed;

		if (dojox.embed.Quicktime.installed == false){
			this.progress(true);
			if(window.confirm(message['quicktime_required'])) location.href = "http://www.apple.com/quicktime/download";
		}
		lorelei.mml.player.loaded();
	},
	// ロード準備完了時の処理。
	loaded : function(){
		this.changed = false;

		this.embed = dojo.byId('embed');
		
		if(this.type=='jsmml' && this._uNum == null){
			try{
				// _uNumの発行
				this._uNum = this.embed._create();
			}catch(e){
				// なんでタイムアウト処理でちゃんと走るようになるんだ！？つーか、この処理下手すると無限ループですが・・・。
				setTimeout(function(){
					lorelei.mml.player.loaded();
				},500);
			}
		}else if (this.type=='midi'){
			
			// QuickTimeのDOMイベント
			// http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_JavaScript/bQTScripting_JavaScri_Document/chapter_1000_section_6.html
			dojo.connect(this.embed, 'qt_play', function(mv){
				lorelei.mml.player.button_disabled(false);
			});

			dojo.connect(this.embed, 'qt_ended', function(mv){
				lorelei.mml.player.stop();
			});
		}
		this.stop();
		this.play();
		this.button_disabled(false);
	},
	// MML再生
	play :function(){
		if (!this.embed && this.changed == true || (this.type=='jsmml' && this._uNum == null)){
			try{
				this.setup();
			}catch(e){
				console.error(e);
			}
		}else if (this.isPause == true){
			this.PlayButton.attr('label',this.PauseLabel);
			this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerPause');
			
			if (this.type == 'jsmml'){
				if (this.isStop == true){
					this.embed._play(this._uNum, this.score);
					this.isStop = false;
				}else{
					this.embed._play(this._uNum);
				}
			}else if (this.type == 'sion'){
				
			}else{
				this.embed.Play();
			}
			this.isPause = false;
		}else{
			this.PlayButton.attr('label',this.PlayLabel);
			this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerPlay');
			if (this.type == 'jsmml'){
				this.embed._pause(this._uNum);
			}else if (this.type == 'sion'){
				
			}else{
				this.embed.Stop();
			}
			this.isPause = true;
		}
		return;
	},
	// MML停止
	stop : function(id){
		if (this.type == 'jsmml'){
			this.embed._stop(this._uNum);
			this.isStop = true;
		}else{
			this.embed.Rewind();
			this.embed.Stop();
		}
		this.PlayButton.attr('label',this.PlayLabel);
		this.PlayButton.attr('iconClass','LoreleiIcon LoreleiIconPlayerPlay');
		this.isPause = true;
		return;
	},
	// 一括ボタン無効化
	button_disabled : function(tf){
		if (tf == true) this.PlayButton.attr('label',this.PlayLabel);
		this.PlayButton.attr('disabled',tf);
		this.StopButton.attr('disabled',tf);
		return;
	},
	// JSONで受け取った配列をFLMML形式に変換する（マルチパート用の処理）
	toFLMMLstr : function(data){
		var track = [];
		for(var i=0; i < data.length; i++) {	// パートごとのループ
			var mml = data[i].Mml;	// MMLデーター
			var inst = data[i].Instrument;	// 楽器データー
			if (inst.isDram == 0){
				var mmls = [mml.ch1,mml.ch2,mml.ch3];
			}else{
				var mmls = [mml.ch1+mml.ch2];
			}
			track[i] = this.MabiMML2MML(i,inst,mmls,mml.pan);
		}
		return track.join("\n");
	},
	// MabinogiのMMLをFLMML形式の命令に変換
	MabiMML2MML : function(track,inst,mmls,pan,sion){
		var noteRevTable = ['C','C+','D','D+','E','F','F+','G','G+','A','A+','B'];
		var default_octave = 4;
		var offset = 1;	// 1オクターブ高くする。

		// inst.id	楽器のID（3MLE互換）
		// inst.flmml_inst FLMML用の音色データー
		// inst.sion_inst SiON用の音色データー
		// inst.isDram ドラムパートか？（ドラムパートだった場合、絶対指定のドラムの音階が入る。そうでない場合0）

		if (inst.isDram == 0) default_octave = default_octave + offset;
		
		// パートの初期設定
		if (sion){
			var output = [
				'/*{'+ inst.name + '}*/',
				'#conf' + track + '=' + inst.sion_inst + ' p'+ pan + ' v8l4o' + default_octave + ';'
			];
		}else{
			var output = [
				'/** '+ inst.name + ' **/',
				'$conf' + track + '=' + inst.flmml_inst + ' @P'+ pan + ' v8l4o' + default_octave + ';'
			];
		}
		
		var part = [];
		for(var partid = 0; partid < mmls.length; partid++) {	// チャンネルごとのループ 418
			var mml_notes = mmls[partid].replace(/\n/g, "");
			mml_notes = mml_notes.match(/[abcdefglnortvABCDEFGLNORTV<>][\+\#-]?[0-9]*\.?&?/g);
			if (mml_notes == null) { continue; }
			var cOctave = default_octave;
			var flmml = [];
			for(var mnid=0; mnid < mml_notes.length; mnid++) {	// 命令ごとのループ
				var mml_note = mml_notes[mnid];
				// 現在のオクターブを取得
				if (inst.isDram != 0){
					if(mml_note.match(/[oO](0|[1-9][0-9]*)/)) {
						flmml[mnid] = '';
					} else if(mml_note.match(/</)) {
						flmml[mnid] = '';
					} else if(mml_note.match(/>/)) {
						flmml[mnid] = '';
					} else if(mml_note.match(/[nN](0|[1-9][0-9]*)/)) {
						flmml[mnid] = 'c';
					} else if(mml_note.match(/([abcdefgABCDEFG])([\+\#-]?)([1-9][0-9]*|0?)(\.?)(&?)/)) {
						flmml[mnid] = 'c' + RegExp.$3 +  RegExp.$4 +  RegExp.$5;
					}else{
						flmml[mnid] = mml_note;
					}
				}else{
					if(mml_note.match(/[oO](0|[1-9][0-9]*)/)) {
						cOctave = parseInt(RegExp.$1) + offset;
						if(inst.isDram == 0){
							flmml[mnid] = 'o'+cOctave;
						}else{
							flmml[mnid] = '';
						}
					} else if(mml_note.match(/</)) {
					// 不等号記号の場合、FLMMLの仕様により、記号をひっくり返す。（#OCTAVE REVERSEを使えば済むけどね）
						cOctave = cOctave-1;
						if(inst.isDram == 0){
							flmml[mnid] = '>';
						}else{
							flmml[mnid] = '';
						}
					} else if(mml_note.match(/>/)) {
						cOctave = cOctave+1;
						if(inst.isDram == 0){
							flmml[mnid] = '<';
						}else{
							flmml[mnid] = '';
						}
					} else if(mml_note.match(/[nN](0|[1-9][0-9]*)/)) {
						var nValue = parseInt(RegExp.$1);
						var Octave = Math.floor(nValue/12) + offset;	// ノート値を12で割った値がオクターブ
						var note = noteRevTable[nValue%12];	// あまりがノート値
						// 上で取得したオクターブが等しければオクターブ命令を省略。
						if (cOctave == Octave){
							flmml[mnid] = note;
						}else{
							if (cOctave == Octave+1){
								flmml[mnid] = ('>'+note+'<');
							}else if (cOctave == Octave-1){
								flmml[mnid] = ('<'+note+'>');
							}else{
								flmml[mnid] = ('o'+Octave+note+'o'+cOctave);
							}
						}
					}else{
						// それ以外の命令は変換しない
						flmml[mnid] = mml_note;
					}
				}
			}
			// 実際のMMLのパートごとの楽譜
			if (sion){
				part[partid] = '#conf' + track + ' ' + flmml.join("") + ";\n";
			}else{
				part[partid] = '$conf' + track + " " + flmml.join("") + ";\n";	// 配列をマージ
			}
			
		}
		return output.join("\n") + "\n" + part.join("");
	},
	// 読み込み中の表示
	progress : function(isFinish){
		try{
			this.embed._destroy(this._uNum());	// JSMMLが呼び出されていた場合削除。（効果あるかは疑問）
		}catch(e){}	// エラー消し（ｗ
		this._uNum = null;
		this.embed = null;	// プラグインのメモリ解放
		if (isFinish == true){
			this.Container.innerHTML = '';
		}else{
			this.button_disabled(true);
			this.Container.innerHTML = '<img src="' + dojo.moduleUrl("lorelei", "resource/loading_bar.gif") + '" width="220" height="19" alt="Now Loading..." />';
		}
	}
};

console.log(lorelei.mml.player.meta.title,lorelei.mml.player.meta.release,' has been loaded.');